﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PSE.Deployment.Engine.Interfaces;
using PSE.Deployment.Engine.CustomEventArgs;
using PSE.Deployment.Engine.Enums;
using System.Threading;
using PSE.Deployment.Engine.CustomExceptions;
using System.IO;
using System.Xml.Serialization;


namespace PSE.Deployment.Engine
{
    /// <summary>
    /// Classe base para contrucao de setup (nivel mais alto de execução)
    /// </summary>
    public class ExecutionEngine : BaseStepGroup
    {
        //public string ParametersFilePath
        //{
        //    get;
        //    set;
        //}

        public ExecutionEngine()
            : base(null)
        {
            //this.Parameter = Activator.CreateInstance<TParm>();
            this.ExecutionFinished += new EventHandler<ExecutionFinishedEventArgs>(Engine_ExecuteFinished);
            this.ExecutionStarted += new EventHandler<ExecutionStartedEventArgs>(Engine_ExecuteStarted);
            this.ExecutionCanceled += new EventHandler<ExecutionCanceledEventArgs>(Engine_Canceled);
            this.ExecutionError += new EventHandler<ExecutionErrorEventArgs>(Engine_ExecuteError);
            this.ExecutionIgnored += new EventHandler<ExecutionIgnoredEventArgs>(Engine_ExecutionIgnored);
            this.UndoError += new EventHandler<UndoErrorEventArgs>(Engine_UndoError);
            
            ExecutionDateTime = DateTime.Now;
        }

        public override void Execute()
        {
            if (this.NavigationBehavior == StepNavigationBehavior.NextPreviousCancel)
                throw new InvalidUsageException(string.Format("O NavigationBehavior NextPreviousCancel não é suportado pela classe Engine, utilize {0}.", StepNavigationBehavior.NextCancel.ToString()));

            this.OnExecutionStarted(new ExecutionStartedEventArgs(this));

            base.Execute();

            //caso o passo seja finalizado como finished
            if (this.ExecutionStatus == StepExecutionStatus.Finished)
            {
                ExecutionFinishedEventArgs e = new ExecutionFinishedEventArgs(this);
                this.OnExecutionFinished(e);
                //se for o modo de navegacao Continuous, nao permitir cancelamento ao final da execucao
                if (e.NavigationAction == StepNavigationAction.Cancel)
                {
                    this.Cancel();
                }
            }
        }

        /// <summary>
        /// Pausa o processamento do step corrente
        /// </summary>
        public void PauseExecution()
        {
            // _continue = false;
            ExecutionControl.Continue.Reset();
            // _isStopped = false;
            ExecutionControl.IsStopped.Reset();
        }

        /// <summary>
        /// Continua o processamento do step corrente
        /// </summary>
        public void ContinueExecution()
        {
            // _isStopped = false;
            ExecutionControl.IsStopped.Reset();
            // _continue = true;
            ExecutionControl.Continue.Set();
        }

        /// <summary>
        /// Interrompe o processamento do step corrente
        /// </summary>
        public void StopExecution()
        {
            // _isStopped = true;
            ExecutionControl.IsStopped.Set();
            // _continue = true;
            ExecutionControl.Continue.Set();
        }

        /// <summary>
        /// Carregar os parâmetros a partir de um arquivo XML
        /// </summary>
        /// <param name="parametersFilePath"></param>
        /// <returns></returns>
        //public void LoadParameters(string parametersFilePath)
        //{
        //    this.ParametersFilePath = parametersFilePath;
        //    if (File.Exists(parametersFilePath))
        //    {
        //        XmlSerializer ser = new XmlSerializer(typeof(TParm));
        //        StreamReader sr = new StreamReader(parametersFilePath);
        //        this.Parameter = (TParm)ser.Deserialize(sr);
        //        sr.Close();
        //    }
        //}

        /// <summary>
        /// Salvar os parâmetros em um arquivo XML
        /// </summary>
        /// <param name="parametersFilePath"></param>
        //public void SaveParameters()
        //{
        //    if (!string.IsNullOrEmpty(this.ParametersFilePath))
        //    {
        //        string directory = Path.GetDirectoryName(this.ParametersFilePath);
        //        if (!Directory.Exists(directory))
        //        {
        //            Directory.CreateDirectory(directory);
        //        }

        //        XmlSerializer ser = new XmlSerializer(typeof(TParm));
        //        using (StreamWriter sw = new StreamWriter(this.ParametersFilePath, false))
        //        {
        //            ser.Serialize(sw, this.Parameter);
        //        }
        //    }
        //}

        //public void DeleteParameters()
        //{
        //    if (!string.IsNullOrEmpty(this.ParametersFilePath))
        //    {
        //        if (File.Exists(this.ParametersFilePath))
        //            File.Delete(this.ParametersFilePath);
        //    }
        //}

        #region Métodos relacionados a gravacao de log de execução
        void Engine_UndoError(object sender, UndoErrorEventArgs e)
        {
            WriteStepLog(e.OriginalSource, "erros ao tentar desfazer.", e.Exception);
        }

        void Engine_ExecutionIgnored(object sender, ExecutionIgnoredEventArgs e)
        {

        }

        void Engine_ExecuteError(object sender, CustomEventArgs.ExecutionErrorEventArgs e)
        {
            //movido para a classe BaseStepGroup para resolver problema de exibicao de messagebox de erro antes de logar
            //WriteStepLog(e.OriginalSource, "finalizado com erros.", e.OriginalSource.ExecutionException);
        }

        void Engine_Canceled(object sender, ExecutionCanceledEventArgs e)
        {
            WriteStepLog(e.OriginalSource, "cancelado.");
        }

        void Engine_ExecuteStarted(object sender, ExecutionStartedEventArgs e)
        {
            WriteStepLog(e.OriginalSource, "iniciado.");
        }

        void Engine_ExecuteFinished(object sender, ExecutionFinishedEventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            string baseMsg = "";

            //se for um grupo
            if (e.OriginalSource is IStepGroup)
            {
                //verificar se ha steps ignorados
                if (CheckIgnoredSteps((IStepGroup)e.OriginalSource))
                {
                    baseMsg = "parciamente finalizado.";
                }
                else
                {
                    baseMsg = "finalizado com sucesso.";
                }
            }
            else
            {
                baseMsg = "finalizado com sucesso.";
            }

            WriteStepLog(e.OriginalSource, baseMsg);
        }

        protected bool CheckIgnoredSteps(IStepGroup stepGroup)
        {
            bool hasIgnoredSteps = false;
            if (stepGroup.IgnoredSteps.Count > 0)
                hasIgnoredSteps = true;
            else
            {
                foreach (IStep step in stepGroup.Steps)
                {
                    if (step is IStepGroup)
                    {
                        hasIgnoredSteps = CheckIgnoredSteps((IStepGroup)step);
                        if (hasIgnoredSteps)
                            return hasIgnoredSteps;
                    }
                }
            }

            return hasIgnoredSteps;
        }

        #endregion
    }
}
